/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/* $Id: mem_pool.c 34 2007-07-09 06:03:33Z kf701.ye $ */

#include "kf701.h"

/**
 * @brief create a memory pool
 * @param slice_size memory slice size
 * @param pre alloc count slice
 * @return mem_pool_t pointer
 **/
mem_pool_t *mem_pool_create(uint32_t slice_size, uint32_t pre_count)
{
	_mem_slice_t *array;
	mem_pool_t *pool;
	int i;

	uint32_t slice_count = CLAMP( pre_count, 10, 100 );

	array = (_mem_slice_t*)malloc(slice_count * sizeof(_mem_slice_t));
	for( i=0; i<slice_count; i++ )
	{
		array[i].use = false;
		array[i].addr = malloc( slice_size );
		memset( array[i].addr, 0, slice_size );
	}

	pool = (mem_pool_t*)malloc(sizeof(mem_pool_t));
	pool->slice_array = array;
	pool->slice_size = slice_size;
	pool->slice_count = slice_count;
	pool->used_count = 0;

	return pool;
}

/**
 * @brief destory a memory pool
 * @param mem_pool_t pointer
 * @return void
 **/
void mem_pool_destory(mem_pool_t *pool)
{
	if( NULL == pool )
		return;

	int i;
	for( i=0; i<pool->slice_count; i++ )
	{
		free( pool->slice_array[i].addr );
	}
	free( pool->slice_array );
	free( pool );
}

static void mem_pool_increase_slice(mem_pool_t *pool)
{
	if( NULL == pool )
		return;

	_mem_slice_t *new_array;
	int new_count = pool->slice_count + 10;

        new_array = (_mem_slice_t*)calloc(new_count, sizeof(_mem_slice_t));
	memcpy( new_array, pool->slice_array, pool->slice_count * sizeof(_mem_slice_t) );

	int i = pool->slice_count;
	for( ; i < new_count; i++ )
	{
		new_array[i].use = false;
		new_array[i].addr = malloc( pool->slice_size );
		memset( new_array[i].addr, 0, pool->slice_size );
	}

	free( pool->slice_array );
	pool->slice_count = new_count;
	pool->slice_array = new_array;
	
	return;
}

/**
 * @brief malloc a slice memory from pool
 **/
void *mem_pool_malloc(mem_pool_t *pool)
{
	if( unlikely(NULL == pool) )
		return NULL;

	if( unlikely(pool->used_count >= pool->slice_count) )
		mem_pool_increase_slice( pool );

	int i;
	for( i=0; i<pool->slice_count; i++ )
	{
		if( false == pool->slice_array[i].use )
		{
			pool->slice_array[i].use = true;
			pool->used_count++;
			return pool->slice_array[i].addr;
		}
	}
	return NULL;
}

/**
 * @brief malloc a slice memory from pool and set zero
 **/
void *mem_pool_malloc0(mem_pool_t *pool)
{
	if( unlikely(NULL == pool) )
		return NULL;

	if( unlikely(pool->used_count >= pool->slice_count) )
		mem_pool_increase_slice( pool );

	int i;
	for( i=0; i<pool->slice_count; i++ )
	{
		if( false == pool->slice_array[i].use )
		{
			pool->slice_array[i].use = true;
			pool->used_count++;
			memset( pool->slice_array[i].addr, 0, pool->slice_size );
			return pool->slice_array[i].addr;
		}
	}
	return NULL;
}

/**
 * @brief free a slice back to pool
 **/
void mem_pool_free(mem_pool_t *pool, void *mem)
{
	if( unlikely(NULL == pool) )
		return;

	int i;
	for( i=0; i<pool->slice_count; i++ )
	{
		if( POINTER_TO_UINT(mem) == 
			POINTER_TO_UINT(pool->slice_array[i].addr) )
		{
			pool->slice_array[i].use = false;
			pool->used_count--;
			return;
		}
	}
}

/**
 * only for debug
 */
void mem_pool_debug(mem_pool_t *pool)
{
	if( NULL == pool )
		return;

	sys_debug("slice_size = %d\n", pool->slice_size);
	sys_debug("slice_count = %d\n", pool->slice_count);
	sys_debug("used_count = %d\n", pool->used_count);
}
